home *** CD-ROM | disk | FTP | other *** search
/ Mac Cube 4: Multimedia Applications / MacCube Volume 4: Multimedia Applications.iso / Graphics / POV Ray / POVSOURCE / SOURCE / ImageWindow.c < prev    next >
Text File  |  1994-02-04  |  40KB  |  1,545 lines

  1. /*==============================================================================
  2. Project:    POV-Ray
  3.  
  4. Version:    2.2
  5.  
  6. File:    ImageWindow.c
  7.  
  8. Description:
  9.     This file contains the Macintosh Image window routines for POV-Ray.
  10. ------------------------------------------------------------------------------
  11. Authors:
  12.     Jim Nitchals, David Harr, Eduard [esp] Schwan
  13. ------------------------------------------------------------------------------
  14.     from Persistence of Vision Raytracer
  15.     Copyright 1993 Persistence of Vision Team
  16. ------------------------------------------------------------------------------
  17.     NOTICE: This source code file is provided so that users may experiment
  18.     with enhancements to POV-Ray and to port the software to platforms other 
  19.     than those supported by the POV-Ray Team.  There are strict rules under
  20.     which you are permitted to use this file.  The rules are in the file
  21.     named POVLEGAL.DOC which should be distributed with this file. If 
  22.     POVLEGAL.DOC is not available or for more info please contact the POV-Ray
  23.     Team Coordinator by leaving a message in CompuServe's Graphics Developer's
  24.     Forum.  The latest version of POV-Ray may be found there as well.
  25.  
  26.     This program is based on the popular DKB raytracer version 2.12.
  27.     DKBTrace was originally written by David K. Buck.
  28.     DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
  29. ------------------------------------------------------------------------------
  30. More Info:
  31.     This Macintosh version of POV-Ray was created and compiled by Jim Nitchals
  32.     (Think 5.0) and Eduard Schwan (MPW 3.2), based (loosely) on the original
  33.     port by Thomas Okken and David Lichtman, with some help from Glenn Sugden.
  34.  
  35.     For bug reports regarding the Macintosh version, you should contact:
  36.     Eduard [esp] Schwan
  37.         CompuServe: 71513,2161
  38.         Internet: jl.tech@applelink.apple.com
  39.         AppleLink: jl.tech
  40.     Jim Nitchals
  41.         Compuserve: 73117,3020
  42.         America Online: JIMN8
  43.         Internet: jimn8@aol.com -or- jimn8@applelink.apple.com
  44.         AppleLink: JIMN8
  45. ------------------------------------------------------------------------------
  46. Change History:
  47.     930610    [esp]    Created
  48.     930610    [esp]    Added Size2Window code
  49.     930620    [esp]    Changed brighten/darken image slightly to fix saturation bug
  50.     930710    [esp]    Added WindowValid check
  51.     930903    [esp]    Major bug fixes to Virtual Image Buffer code
  52.     931001    [esp]    version 2.0 finished (Released on 10/4/93)
  53.     931119    [djh]    2.0.1 source conditionally compiles for PPC machines, keyword __powerc
  54. ==============================================================================*/
  55.  
  56. /*==== our header ====*/
  57. #include "ImageWindow.h"
  58.  
  59.  
  60. /*==== defs ====*/
  61.  
  62. // temp virtual image file file
  63. #define    POVRAY_IMAGETEMP_FNAME    "POV-Ray.vib"
  64.  
  65. // Windows
  66. #define    kWindID_Image            1001    // image window
  67.  
  68.  
  69. #define    PICTF_HEADER_SIZE        512        // old MacDraw PICTF header
  70.  
  71. /* Pict file header structure (after the 512 byte header) */
  72. typedef struct
  73. {
  74.     short        picSize;        // low word of size
  75.     Rect        picFrame;        // picture bounds
  76. } PictFHeader, *PictFHeaderPtr;
  77.  
  78.  
  79. /*==== globals ====*/
  80.  
  81. WindowPtr        gImageWindowPtr = NULL;        // the image window
  82. Boolean            gImageWindIsValid = false;    // true if valid stuff in window
  83. // Str63            gImageWindName;                // image window's name
  84. Boolean            use_custom_palette = false;    // does the user desire to use color q?
  85. int                gColorQuantMethod = -1;        // what kind of color quantization?
  86. Boolean            gDoingVirtualFile = false;    // true if doing virtual image buffering
  87.  
  88. #if defined(__powerc)
  89. extern RoutineDescriptor gPutPICTRD;
  90. #endif
  91.  
  92. /*==== globals (local) ====*/
  93.  
  94. /* image window stuff */
  95. static PixMapHandle    gOffScreenPixMapH = NULL;
  96. static Ptr            gImagePixmapPtr = NULL;
  97. static CTabHandle    gCTabHdl = NULL;
  98.  
  99. static PaletteHandle    gCustomPalette = NULL;
  100. static PaletteHandle    gSystemPalette = NULL;
  101.  
  102. // used by Paint_to_Picture() and MyPutPicProc()
  103. PicHandle            gMyPicHandle;
  104. long                gMyPicSize;
  105.  
  106. static FILE            *gImagePictFile = NULL; // used to save image to PICT
  107. static Rect            gImageBounds,        // source (offscreen image) bounds
  108.                     gDisplayBounds;        // destination image bounds
  109. static short        gImageWidth,
  110.                     gImageHeight;
  111. static int            gLastYPos = 0;        // last vertical position during previous refresh
  112. static short        gCurrYPos;
  113. static long            gPixMapBuffSize;
  114. static long            gDitherTicks;        // # of ticks to wait between image refresh
  115. static long            gRefreshTick = 0;    // Time of last Quickdraw dither of graphic port
  116.  
  117. /* Undo stuff */
  118. static Ptr            gImageUndoBuffer = NULL;
  119. static Ptr            gImageUndoBuffer2 = NULL;
  120. static Ptr            gImageRevertBuffer = NULL;
  121.  
  122. /* virtual image buffer globals */
  123. static FILE            *gVirtualImageFile = NULL;
  124. static short        virtual_pixHeight;
  125. static short        virtual_buffer_dirty;
  126. static short        virtual_currentSegment = -1;
  127. static short        virtual_minY;
  128. static short        virtual_maxY;
  129. static long            virtual_segSize = SWAP_SIZE;
  130.  
  131.  
  132. // ==============================================
  133. void InitImageWindow(void)
  134. {
  135.     // allocate the image window (it is shown/hidden later)
  136.     gImageWindowPtr = GetNewCWindow(kWindID_Image, NULL, (WindowPtr)NULL);
  137. } // InitImageWindow
  138.  
  139.  
  140. // ==============================================
  141. void KillImageWindow(void)
  142. {
  143.     if (gImageWindowPtr)
  144.         DisposeWindow(gImageWindowPtr);
  145.     gImageWindowPtr = NULL;
  146. } // KillImageWindow
  147.  
  148.  
  149.  
  150. // ==============================================
  151. // Create system/custom palette variables for use by image window
  152. void SetupPalettes(void)
  153. {
  154.     int        k;
  155.  
  156.     // Get default System palette for non-custom mode, (IM VI, Pg. 20-17)
  157.     gSystemPalette = NewPalette(256, NULL, pmTolerant, 0x0000);
  158.     if (gSystemPalette)
  159.     {
  160.         CopyPalette(GetPalette((WindowPtr)-1), gSystemPalette, 0, 0, 256);
  161.         for (k=0; k<256; k++)
  162.             SetEntryUsage(gSystemPalette, k, pmTolerant, 0x0000);
  163.     }
  164.  
  165.     // allocate a palette for the Image window, fill to default system palette
  166.     gCustomPalette = NewPalette(256, NULL, pmTolerant, 0x0000); // exact match only
  167.     if (gCustomPalette)
  168.     {
  169.         CopyPalette(gSystemPalette, gCustomPalette, 0, 0, 256);
  170.         for (k=0; k<256; k++)
  171.             SetEntryUsage(gCustomPalette, k, pmTolerant, 0x0000);
  172.     }
  173. } // SetupPalettes
  174.  
  175.  
  176. // ==============================================
  177. // Build offscreen pixmap
  178. void SetupOffscreen(void)
  179. {
  180.     // Jim, since we REQUIRE 32 bit Color QuickDraw, we should change this
  181.     // to use the much easier and better supported 32bQD call NewGWorld()? [esp]
  182.     gCTabHdl                = (CTabHandle) NewHandle(sizeof(ColorTable));
  183.     (**gCTabHdl).ctSeed        = 24;
  184.     (**gCTabHdl).ctFlags    = 0;
  185.     (**gCTabHdl).ctSize        = 0;
  186.     gOffScreenPixMapH        = (PixMapHandle) NewHandle(sizeof(PixMap));
  187.     if (gOffScreenPixMapH != NULL)
  188.     {
  189.         (**gOffScreenPixMapH).pmVersion        = 0;
  190.         (**gOffScreenPixMapH).packType        = 0;
  191.         (**gOffScreenPixMapH).packSize        = 0;
  192.         (**gOffScreenPixMapH).hRes            = 0x480000;    // 72.0 x 72.0 dpi
  193.         (**gOffScreenPixMapH).vRes            = 0x480000;
  194.         (**gOffScreenPixMapH).pixelType        = RGBDirect;
  195.         (**gOffScreenPixMapH).pixelSize        = 32;
  196.         (**gOffScreenPixMapH).cmpCount        = 3;
  197.         (**gOffScreenPixMapH).cmpSize        = 8;
  198.         (**gOffScreenPixMapH).planeBytes    = 0;
  199.         (**gOffScreenPixMapH).pmTable        = gCTabHdl;
  200.         (**gOffScreenPixMapH).pmReserved    = 0;
  201.         (**gOffScreenPixMapH).baseAddr        = NULL;
  202.     }
  203.     
  204.     if (gOffScreenPixMapH == NULL)
  205.     {
  206.         // fatal error
  207.         displayDialog(kdlog_GenericFatalErr, "Cannot allocate memory for PixMap",
  208.                     MemError(), ewcDoCentering, eMainDevice);
  209.         exit_handler();
  210.     }
  211. } // SetupOffscreen
  212.  
  213.  
  214. // ==============================================
  215. // Build offscreen pixmap
  216. void KillOffscreen(void)
  217. {
  218.     if (gCTabHdl)
  219.         DisposeHandle((Handle)gCTabHdl);
  220.     if (gOffScreenPixMapH)
  221.         DisposeHandle((Handle)gOffScreenPixMapH);
  222.     gCTabHdl = NULL;
  223.     gOffScreenPixMapH = NULL;
  224. } // KillOffscreen
  225.  
  226.  
  227. // ==============================================
  228. // Close the image window
  229. void CloseImageWindow()
  230. {
  231.     if (gImageWindowPtr)
  232.         HideWindow(gImageWindowPtr);
  233.     (**gFilePrefs_h).imageMagFactor = viewmn_hidden;
  234. } // CloseImageWindow
  235.  
  236.  
  237. // ==============================================
  238. // Force the entire image window to be updated
  239. void InvalRect_ImageWindow(Boolean DoWholeWindow)
  240. {
  241.     int        magFactor;
  242.     Rect    myInvalRect;
  243.  
  244.     if (gImageWindowPtr && ((WindowPeek)gImageWindowPtr)->visible)
  245.     {
  246.         SetPort(gImageWindowPtr);
  247.  
  248.         myInvalRect = gImageWindowPtr->portRect;
  249.         ClipRect(&myInvalRect);
  250.  
  251.         // if doing partial window in a regular magnification mode...
  252.         if (!DoWholeWindow && ((**gPrefs2Use_h).imageMagFactor != viewmn_Size2Window))
  253.         {
  254.             magFactor = (**gPrefs2Use_h).imageMagFactor-viewmn_normal+1; // 1,2,3,4
  255.             myInvalRect.top = magFactor * gLastYPos;
  256.             myInvalRect.bottom = magFactor * (gCurrYPos + 1);
  257.         }
  258.  
  259.         InvalRect(&myInvalRect);
  260.     }
  261. } // InvalRect_ImageWindow
  262.  
  263.  
  264. // ==============================================
  265. // Display the image window grow box
  266. static void DrawImageGrowBox(void)
  267. {
  268.     int            looper;
  269.     Rect        box;
  270.     PenState    pstate;
  271.  
  272.     // Save current port state
  273.     SetPort(gImageWindowPtr);
  274.     GetPenState(&pstate);
  275.  
  276.     // find window size
  277.     box = gImageWindowPtr->portRect;
  278.  
  279.     // make boxes in lower left corner
  280.     box.top = box.bottom-7;
  281.     box.left = box.right-7;
  282.     OffsetRect(&box, -1, -1);
  283.  
  284.     // do two overlapping boxes
  285.     for (looper=0; looper<2; looper++)
  286.     {
  287.         // draw white box
  288.         ForeColor(whiteColor);
  289.         FrameRect(&box);
  290.  
  291.         // draw black box
  292.         OffsetRect(&box, -1, -1);
  293.         ForeColor(blackColor);
  294.         FrameRect(&box);
  295.  
  296.         // move up & draw 2nd smaller box
  297.         OffsetRect(&box, -2, -2);
  298.         box.right    -= 1;
  299.         box.bottom    -= 1;
  300.     }
  301.  
  302.     // restore world
  303.     SetPenState(&pstate);
  304. } // DrawImageGrowBox
  305.  
  306.  
  307. // ==============================================
  308. void DoResizeImageWindow(WindowPtr w, short h, short v)
  309. {
  310. //    Rect    oldHorizBar;
  311.     Rect     r;
  312.     
  313.     SetPort(w);
  314.  
  315. //    oldHorizBar = w->portRect;
  316. //    oldHorizBar.top = oldHorizBar.bottom - (SBarWidth+1);
  317.  
  318.     SizeWindow(w, h, v, false);
  319.  
  320. //    EraseRect(&oldHorizBar);
  321.     
  322. //    MoveControl(gSrcWind_VScroll, w->portRect.right - SBarWidth, w->portRect.top-1);
  323. //    SizeControl(gSrcWind_VScroll, SBarWidth+1, w->portRect.bottom - w->portRect.top-(SBarWidth-2));
  324. //    r = (**gSrcWind_VScroll).contrlRect;
  325. //    ValidRect(&r);
  326.  
  327.     GetGlobalWindowRect(w, &r);
  328.     (**gFilePrefs_h).imageWind_pos = r;
  329.     if ((**gPrefs2Use_h).imageMagFactor == viewmn_Size2Window)
  330.         gDisplayBounds = w->portRect;
  331.  
  332. } // DoResizeImageWindow
  333.  
  334.  
  335. // ==============================================
  336. void DoGrowImageWindow(WindowPtr w, Point p)
  337. {
  338.     GrafPtr        savePort;
  339.     long        theResult;
  340.     Rect        r;
  341.     
  342.     GetPort(&savePort);
  343.     SetPort(w);
  344.  
  345.     GetMaxGrowRect(w, &r);    
  346.     theResult = GrowWindow(w, p, &r);
  347.     if (theResult != 0)
  348.     {
  349.         DoResizeImageWindow(w, LoWord(theResult), HiWord(theResult));
  350.         InvalRect_ImageWindow(true);
  351.     }
  352.  
  353.     SetPort(savePort);
  354. } // DoGrowImageWindow
  355.  
  356.  
  357. // ==============================================
  358. void UpdateImageWindow(void)
  359. {
  360.     if (gImageWindowPtr)
  361.     {
  362.         SetPort(gImageWindowPtr);
  363.         BeginUpdate(gImageWindowPtr);
  364.         DrawImageWindow(false);
  365.         EndUpdate(gImageWindowPtr);
  366.     }
  367. } // UpdateImageWindow
  368.  
  369.  
  370. // ==============================================
  371. void SetImageWindowMag(short magMenuItem)
  372. {
  373.     int            magFactor;
  374.     Rect        r,screenRect;
  375.     GDHandle    theGD;
  376.  
  377.     if (magMenuItem == viewmn_hidden)
  378.         HideWindow(gImageWindowPtr);
  379.     else
  380.     {
  381.         if (magMenuItem == viewmn_Size2Window)
  382.         { // set image buffer to 1xsize, leave window size alone
  383.             gDisplayBounds = gImageWindowPtr->portRect;
  384.         }
  385.         else
  386.         { // set image buffer rect to N*Size, resize window
  387.             magFactor = magMenuItem-viewmn_normal+1; // 1,2,3,4
  388.             // get new zero-based rect
  389.             SetRect(&gDisplayBounds, 0, 0,
  390.                         gImageWidth * magFactor,
  391.                         gImageHeight * magFactor);
  392.             // convert to potential global window rect
  393.             GetGlobalWindowRect(gImageWindowPtr, &r);
  394.             r.right = r.left + gDisplayBounds.right;
  395.             r.bottom = r.top + gDisplayBounds.bottom;
  396.             // move it to appropriate screen if needed
  397.             ForceRectOnScreen(&r);
  398. // [esp] hmm, shouldn't this next useful chunk of code be moved into ScreenUtils.c?
  399.             // clip any overhang off bottom/right
  400.             theGD = GetClosestGDevice(&r);
  401.             screenRect = (**theGD).gdRect;
  402.             if (r.right > screenRect.right)
  403.                 r.right = screenRect.right;
  404.             if (r.bottom > screenRect.bottom)
  405.                 r.bottom = screenRect.bottom;
  406.             MoveWindow(gImageWindowPtr, r.left, r.top, false);
  407.             DoResizeImageWindow(gImageWindowPtr,
  408.                         r.right-r.left,
  409.                         r.bottom-r.top);
  410. // [esp] need code here to handle scroll bars... oh yah, we need scroll bars first!
  411.         }
  412.  
  413.         // if window is valid, show it now.  This check lets the user
  414.         // set the window size even when there's no valid window content,
  415.         // and doesn't actually show the window until it is valid.
  416.         if (gImageWindIsValid)
  417.         {
  418.             ShowWindow(gImageWindowPtr);
  419.             SelectWindow(gImageWindowPtr);
  420.             InvalRect_ImageWindow(true);
  421.         }
  422.     } // else visible
  423. } // SetImageWindowMag
  424.  
  425.  
  426. // ==============================================
  427. // Create undo buffers for Image window changes
  428. void make_undo(void)
  429. {
  430.     if (gImageUndoBuffer == 0)
  431.     {
  432.         gImageUndoBuffer = NewPtr (gPixMapBuffSize);    /* room for image */
  433.         gImageUndoBuffer2 = NewPtr (gPixMapBuffSize);    /* room for image */
  434.         gImageRevertBuffer = NewPtr (gPixMapBuffSize);
  435.         if ( (gImageRevertBuffer) && (gImagePixmapPtr))
  436.             memcpy(gImageRevertBuffer, gImagePixmapPtr, gPixMapBuffSize);
  437.     }
  438.  
  439.     /* if any of the memory allocations failed, or available mem is low, */
  440.     /* fail the whole undo system */
  441.     if ( (gImageUndoBuffer == NULL) || (gImageUndoBuffer2 == NULL) || (gImageRevertBuffer == NULL)
  442.     || (FreeMem() < 50000L) )
  443.     {
  444.         if (gImageUndoBuffer)
  445.             DisposePtr (gImageUndoBuffer);
  446.         if (gImageUndoBuffer2)
  447.             DisposePtr (gImageUndoBuffer2);
  448.         if (gImageRevertBuffer)
  449.             DisposePtr (gImageRevertBuffer);
  450.         gImageUndoBuffer = NULL;
  451.         gImageUndoBuffer2 = NULL;
  452.         gImageRevertBuffer = NULL;
  453.     }
  454.  
  455.     if ((gImageUndoBuffer) && (gImagePixmapPtr))
  456.     {
  457.         memcpy(gImageUndoBuffer, gImagePixmapPtr, gPixMapBuffSize);
  458.         gCanUndo = TRUE;
  459.     }
  460. } // make_undo
  461.  
  462.  
  463. // ==============================================
  464. // Actually do the undo operation on the image window
  465. void undo_image(void)
  466. {
  467.     if ((gImageUndoBuffer) && (gCanUndo) && (gImagePixmapPtr))
  468.     {
  469.         memcpy(gImageUndoBuffer2, gImagePixmapPtr, gPixMapBuffSize);
  470.         memcpy(gImagePixmapPtr, gImageUndoBuffer, gPixMapBuffSize);
  471.         memcpy(gImageUndoBuffer, gImageUndoBuffer2, gPixMapBuffSize);
  472.         InvalRect_ImageWindow(true);
  473.     }
  474. } // undo_image
  475.  
  476.  
  477. // ==============================================
  478. // restore the original image to the image window
  479. void revert_image(void)
  480. {
  481.     if ((gImageRevertBuffer) && (gImagePixmapPtr))
  482.     {
  483.         memcpy(gImageUndoBuffer, gImagePixmapPtr, gPixMapBuffSize);
  484.         memcpy(gImagePixmapPtr, gImageRevertBuffer, gPixMapBuffSize);
  485.         gCanUndo = TRUE;
  486.         InvalRect_ImageWindow(true);
  487.     }
  488. } // revert_image
  489.  
  490.  
  491. // ==============================================
  492. // return the value "v", insuring it is between 0 and 255
  493. static short ClipToByteRange(short v)
  494. {
  495.     if ( v < 0)
  496.         return (0);
  497.     if (v > 255)
  498.         return (255);
  499.     return (v);
  500. } // ClipToByteRange
  501.  
  502.  
  503. // ==============================================
  504. // Darken every pixel value in image window by 7/8ths original
  505. void darken_image(void)
  506. {
  507.     unsigned char *pptr;
  508.     short        v;
  509.     long        i,j;
  510.     
  511.     make_undo();
  512.     if (gImagePixmapPtr == 0) return;
  513.  
  514.     for (i=0; i < gImageHeight; i++)
  515.     {
  516.         for (j=0; j < gImageWidth; j++)
  517.         {
  518.             pptr = (unsigned char *) 
  519.                     (gImagePixmapPtr + 4L * ((gImageWidth * i) + j));
  520.             *pptr = 0;
  521.             pptr++;
  522.             v = (*pptr * 7 / 8)-1;
  523.             *pptr = ClipToByteRange (v);
  524.             pptr++;
  525.             v = (*pptr * 7 / 8)-1;
  526.             *pptr = ClipToByteRange (v);
  527.             pptr++;
  528.             v = (*pptr * 7 / 8)-1;
  529.             *pptr = ClipToByteRange (v);
  530.             pptr++;
  531.         }
  532.     }
  533.     InvalRect_ImageWindow(true);
  534. } // darken_image
  535.  
  536.  
  537. // ==============================================
  538. // Lighten every pixel value in image window by 8/7ths original
  539. void lighten_image(void)
  540. {
  541.     unsigned char *pptr;
  542.     long i,j;
  543.     short v;
  544.  
  545.     make_undo();
  546.     if (gImagePixmapPtr == 0) return;
  547.  
  548.     for (i=0; i < gImageHeight; i++)
  549.     {
  550.         for (j=0; j < gImageWidth; j++)
  551.         {
  552.             pptr = (unsigned char *) 
  553.                     (gImagePixmapPtr + 4L * ((gImageWidth * i) + j));
  554.             *pptr = 0;
  555.             pptr++;
  556.             v = (*pptr * 8 / 7)+1;
  557.             *pptr = ClipToByteRange (v);
  558.             pptr++;
  559.             v = (*pptr * 8 / 7)+1;
  560.             *pptr = ClipToByteRange (v);
  561.             pptr++;
  562.             v = (*pptr * 8 / 7)+1;
  563.             *pptr = ClipToByteRange (v);
  564.             pptr++;
  565.         }
  566.     }
  567.     InvalRect_ImageWindow(true);
  568. } // lighten_image
  569.  
  570.  
  571. // ==============================================
  572. // Invert every pixel value in image window
  573. void invert_image(void)
  574. {
  575.     unsigned char *pptr;
  576.     long i,j;
  577.     
  578.     make_undo();
  579.     if (gImagePixmapPtr == 0) return;
  580.  
  581.     for (i=0; i < gImageHeight; i++)
  582.     {
  583.         for (j=0; j < gImageWidth; j++)
  584.         {
  585.             pptr = (unsigned char *) 
  586.                     (gImagePixmapPtr + 4L * ((gImageWidth * i) + j));
  587.             *pptr = 0;
  588.             pptr++;
  589.             *pptr = 255 - *pptr;
  590.             pptr++;
  591.             *pptr = 255 - *pptr;
  592.             pptr++;
  593.             *pptr = 255 - *pptr;
  594.             pptr++;
  595.         }
  596.     }
  597.     InvalRect_ImageWindow(true);
  598. } // invert_image
  599.  
  600.  
  601. // ==============================================
  602. // Reduce the contrast of every pixel value in image window a bit
  603. void reduce_contrast(void)
  604. {
  605.     unsigned char *pptr;
  606.     long i,j;
  607.     short v;
  608.     
  609.     make_undo();
  610.     if (gImagePixmapPtr == 0) return;
  611.  
  612.     for (i=0; i < gImageHeight; i++)
  613.     {
  614.         for (j=0; j < gImageWidth; j++)
  615.         {
  616.             pptr = (unsigned char *) 
  617.                     (gImagePixmapPtr + 4L * ((gImageWidth * i) + j));
  618.             *pptr++ = 0;
  619.  
  620.             v = *pptr;
  621.             v = v - ((v - 128) >> 2);
  622.             *pptr++ = v;
  623.  
  624.             v = *pptr;
  625.             v = v - ((v - 128) >> 2);
  626.             *pptr++ = v;
  627.  
  628.             v = *pptr;
  629.             v = v - ((v - 128) >> 2);
  630.             *pptr++ = v;
  631.         }
  632.     }
  633.     InvalRect_ImageWindow(true);
  634. } // reduce_contrast
  635.  
  636.  
  637. // ==============================================
  638. // Increase the contrast of every pixel value in image window a bit
  639. void increase_contrast(void)
  640. {
  641.     unsigned char *pptr;
  642.     long i,j;
  643.     short v;
  644.  
  645.     make_undo();
  646.     if (gImagePixmapPtr == 0) return;
  647.  
  648.     for (i=0; i < gImageHeight; i++)
  649.     {
  650.         for (j=0; j < gImageWidth; j++)
  651.         {
  652.             pptr = (unsigned char *) 
  653.                     (gImagePixmapPtr + 4L * ((gImageWidth * i) + j));
  654.             *pptr++ = 0;
  655.  
  656.             v = *pptr;
  657.             v = v + ((v - 128) >> 2);
  658.             *pptr++ = ClipToByteRange(v);
  659.  
  660.             v = *pptr;
  661.             v = v + ((v - 128) >> 2);
  662.             *pptr++ = ClipToByteRange(v);
  663.  
  664.             v = *pptr;
  665.             v = v + ((v - 128) >> 2);
  666.             *pptr++ = ClipToByteRange(v);
  667.         }
  668.     }
  669.     InvalRect_ImageWindow(true);
  670. } // increase_contrast
  671.  
  672.  
  673. // ==============================================
  674. // draw a 1 pixel black border around the edge of the image
  675. void draw_border(void)
  676. {
  677.     unsigned char *pptr;
  678.     long i;
  679.     
  680.     make_undo();
  681.     if (gImagePixmapPtr == 0) return;
  682.  
  683.     for (i=0; i < gImageHeight; i++)
  684.     {
  685. /* left border edge */
  686.             pptr = (unsigned char *) 
  687.                     (gImagePixmapPtr + (gImageWidth * 4 * i) );
  688.             *pptr++ = 0;
  689.             *pptr++ = 0;
  690.             *pptr++ = 0;
  691.             *pptr++ = 0;
  692.  
  693. /* right border edge */
  694.             pptr = (unsigned char *) 
  695.                     (gImagePixmapPtr + (gImageWidth * 4 * i) + ((gImageWidth-1) * 4) );
  696.             *pptr++ = 0;
  697.             *pptr++ = 0;
  698.             *pptr++ = 0;
  699.             *pptr++ = 0;
  700.     }
  701.     for (i=0; i < gImageWidth; i++)
  702.     {
  703. /* top border edge */
  704.             pptr = (unsigned char *) 
  705.                     (gImagePixmapPtr + (4 * i));
  706.             *pptr++ = 0;
  707.             *pptr++ = 0;
  708.             *pptr++ = 0;
  709.             *pptr++ = 0;
  710.  
  711. /* bottom border edge */
  712.             pptr = (unsigned char *) 
  713.                     ( gImagePixmapPtr + 4L*gImageWidth*(gImageHeight-1) + 4*i );
  714.             *pptr++ = 0;
  715.             *pptr++ = 0;
  716.             *pptr++ = 0;
  717.             *pptr++ = 0;
  718.  
  719.     }
  720.     InvalRect_ImageWindow(true);
  721. } // draw_border
  722.  
  723.  
  724. // ==============================================
  725. // Create and open the virtual image buffer file for large images
  726. void open_virtual(void)
  727. {
  728.     int err;
  729.  
  730.     delete_virtual();
  731.     if (!gDoingVirtualFile)
  732.     {
  733.         gVirtualImageFile = fopen(POVRAY_IMAGETEMP_FNAME,"wb+");
  734.         err = ferror(gVirtualImageFile);
  735.         if (!gVirtualImageFile)
  736.             printf("## Error #%d opening file '%s'!\n", err, POVRAY_IMAGETEMP_FNAME);
  737.     }
  738.     virtual_currentSegment = -1;
  739.     virtual_buffer_dirty = false;
  740.     virtual_minY = 32767;
  741.     virtual_maxY = 0;
  742.     virtual_pixHeight = virtual_segSize / (gImageWidth * 4L);
  743.     gDoingVirtualFile = true;
  744. } // open_virtual
  745.  
  746.  
  747. // ==============================================
  748. // Close and delete the virtual image buffer file
  749. void delete_virtual(void)
  750. {
  751.     int k;
  752.     if (gDoingVirtualFile)
  753.     {
  754.         fclose (gVirtualImageFile);
  755.         gVirtualImageFile = NULL;
  756.     }
  757.     k = remove(POVRAY_IMAGETEMP_FNAME);
  758.     gDoingVirtualFile = false;
  759. } // delete_virtual
  760.  
  761.  
  762. // ==============================================
  763. // Dispose of virtual image buffer memory, and close/delete the file
  764. void dispose_virtual(void)
  765. {
  766.     Handle h;
  767.  
  768.     if (gImagePixmapPtr)
  769.     {
  770.         h = RecoverHandle(gImagePixmapPtr);
  771.         HUnlock(h);
  772.         DisposHandle(h);
  773.         gImagePixmapPtr = NULL;
  774.         gCanUndo = 0;
  775.     }
  776.  
  777.     if (gImageUndoBuffer)
  778.         DisposePtr(gImageUndoBuffer);
  779.     if (gImageUndoBuffer2)
  780.         DisposePtr(gImageUndoBuffer2);
  781.     if (gImageRevertBuffer)
  782.         DisposePtr(gImageRevertBuffer);
  783.     gImageUndoBuffer = NULL;
  784.     gImageUndoBuffer2 = NULL;
  785.     gImageRevertBuffer = NULL;
  786.  
  787.     delete_virtual();
  788.     virtual_minY = 32767;
  789. } // dispose_virtual
  790.  
  791.  
  792. // ==============================================
  793. // swap in the "y"th virtual image buffer segment from disk
  794. void swap_virtual_segment (short y)
  795. {
  796.     int i;
  797.     int err;
  798.         
  799.     // is Y outside currently loaded segment? need to load new one
  800.     if (gDoingVirtualFile && ((y < virtual_minY) || (y >= virtual_maxY)) )
  801.     {
  802.         // is current segment dirty? save to disk first if so
  803.         if (virtual_buffer_dirty && (virtual_currentSegment >= 0))
  804.         {
  805.             fseek(gVirtualImageFile, (virtual_currentSegment * virtual_segSize), SEEK_SET);
  806.             if ( fwrite((char *) gImagePixmapPtr, virtual_segSize, 1, gVirtualImageFile) != 1)
  807.             {    // error
  808.                 err = ferror(gVirtualImageFile);
  809.                 if (err)
  810.                     printf("## Error #%d writing position #%ld in file '%s'!\n",
  811.                             err, (virtual_currentSegment * virtual_segSize),
  812.                             POVRAY_IMAGETEMP_FNAME);
  813.                 dispose_virtual();
  814.             }
  815.         }
  816.  
  817.         // Now calculate and read needed segment into memory
  818.         if (gDoingVirtualFile)
  819.         {
  820.             // calculate new current segment stuff
  821.             virtual_currentSegment = (int) (y / virtual_pixHeight);
  822.             virtual_minY = virtual_currentSegment * virtual_pixHeight;
  823.             virtual_maxY = (virtual_currentSegment + 1) * virtual_pixHeight;
  824.  
  825.             // read it in off disk
  826.             fseek(gVirtualImageFile, (virtual_currentSegment * virtual_segSize), SEEK_SET);
  827.             i = fread((char *) gImagePixmapPtr, virtual_segSize, 1, gVirtualImageFile);
  828.             err = ferror(gVirtualImageFile);
  829.             if (err)
  830.                 printf("## Error #%d reading position #%ld in file '%s'!\n",
  831.                         err, (virtual_currentSegment * virtual_segSize),
  832.                         POVRAY_IMAGETEMP_FNAME);
  833.             virtual_buffer_dirty = false;
  834.         }
  835.  
  836.         // error.. couldn't keep swap file open
  837.         if (!gDoingVirtualFile)
  838.             (void)displayDialog(139, NULL, 0, ewcDoCentering, eSameAsPassedWindow);
  839.     }
  840.  
  841. } // swap_virtual_segment
  842.  
  843.  
  844. // ==============================================
  845. // Initialize the display, set up buffers (VIB if needed)
  846. void display_init(int width, int height)
  847. {
  848.     long        *p, *q, pmsize, i;
  849.     Handle        h;
  850.     short        error_dlogID = 0;
  851.     DialogPtr    progressDialogPtr = NULL;
  852.  
  853.     if ((**gPrefs2Use_h).progress >= kProgMinimal)
  854.     {
  855.         printf("-- [Memory]  FreeMemory=%ldK\n", FreeMem()/1024L);
  856.     }
  857.  
  858.     SetCursor(&gWaitCursor); // could take a little while..
  859.  
  860.     gImageWidth = width;
  861.     gImageHeight = height;
  862.     gPixMapBuffSize = 4L * (long) width * (long) height;
  863.     gCurrYPos = 0;
  864.     gLastYPos = 0;
  865.  
  866.     SetRect(&gImageBounds, 0, 0, width, height);
  867.     pmsize = gPixMapBuffSize;
  868.  
  869.     virtual_buffer_dirty = false;
  870.  
  871.     h = NULL;
  872.     // if there is enough room for a pixmap, plus a little free space (100KB),
  873.     // then go ahead and allocate it..
  874.     if    (FreeMem() > pmsize + 100000L)
  875.         h = NewHandleClear(pmsize);
  876.  
  877.     // ..otherwise play virtual buffer games..
  878.     if (h == NULL)
  879.     {
  880.         // can't allocate, do it in segments, set up progress dialog
  881.         progressDialogPtr = GetNewProgressDialog(153, 3);
  882.         if (progressDialogPtr)
  883.         {
  884.             PositionWindow(progressDialogPtr, ewcDoCentering, eSameAsPassedWindow, (WindowPtr)gp2wWindow);
  885.             ShowWindow(progressDialogPtr);
  886.             SelectWindow(progressDialogPtr);
  887.             DrawDialog(progressDialogPtr);
  888.             // make sure events get through so dlog is updated
  889.             Cooperate(true);
  890.         }
  891.     }
  892.  
  893.     // let user know some info
  894.     if ((**gPrefs2Use_h).progress >= kProgMinimal)
  895.     {
  896.         if ( (h == NULL) && ((**gPrefs2Use_h).progress >= kProgDebug) )
  897.             printf("-d VIBNumSegments=%d, VIBSegmentSize=%dK\n",
  898.                     (int)1+(gImageHeight / (virtual_segSize / (gImageWidth * 4L))),
  899.                     (int)(virtual_segSize/1024L));
  900.         printf("-- ImageBufferPixWidth=%d, ImageBufferPixHeight=%d, ImageBufferSize=%ldK\n",
  901.             gImageWidth, gImageHeight, pmsize/1024L);
  902.     }
  903.  
  904.     if (h)
  905.     { // got whole thing, initialize it
  906.         MoveHHi(h);
  907.         HLock(h);
  908.         gImagePixmapPtr = *h;
  909.         (*gOffScreenPixMapH)->bounds = gImageBounds;
  910.         (*gOffScreenPixMapH)->rowBytes = (width * 4) | 0x8000;
  911.         (*gOffScreenPixMapH)->baseAddr = gImagePixmapPtr;
  912.         // fill to white
  913.         q = (long *) ((long) gImagePixmapPtr + pmsize);
  914.         for (p = (long *) gImagePixmapPtr; p < q; *p++ = -1L)
  915.             ;
  916.         gDitherTicks = 20*60;    /* 20 seconds between re-dithers */
  917.     }
  918.     else
  919.     { // set up the virtual buffer
  920.  
  921.         open_virtual();
  922.         SetRect(&gImageBounds, 0, 0, width, virtual_pixHeight);
  923.  
  924.         if (gDoingVirtualFile)
  925.             h = NewHandleClear(virtual_segSize);
  926.  
  927.         if (!h)
  928.         {
  929.             dispose_virtual();
  930.             error_dlogID = 133; // no display memory
  931.         }
  932.         else
  933.         {
  934.             if (progressDialogPtr)
  935.                 updateProgressDialog(progressDialogPtr, 0, gImageHeight-1, 1);
  936.             MoveHHi(h);
  937.             HLock(h);
  938.             gImagePixmapPtr = *h;
  939.             (*gOffScreenPixMapH)->bounds = gImageBounds;
  940.             (*gOffScreenPixMapH)->rowBytes = (width * 4) | 0x8000;
  941.             (*gOffScreenPixMapH)->baseAddr = gImagePixmapPtr;
  942.             // fill to white
  943.             q = (long *) ((long) gImagePixmapPtr + virtual_segSize);
  944.             for (p = (long *) gImagePixmapPtr; p < q; *p++ = -1)
  945.                 ;
  946.             gDitherTicks = 4L*60L*60L;    /* 4 minutes between re-dithers */
  947.  
  948.             // We go through this for loop with i stepping down through the image
  949.             // scanlines every 1/2 buffer size.  The only reason for not going
  950.             // every buffer size (virtual_pixHeight) is to make the progress bar
  951.             // a little less jerky, doesn't slow things down much.
  952.             for (i=0; (i<gImageHeight)&&gDoingVirtualFile; i+=(virtual_pixHeight>>1))
  953.             {
  954.                 // Update the progress bar
  955.                 if (progressDialogPtr)
  956.                 {
  957.                     Cooperate(true);
  958.                     updateProgressDialog(progressDialogPtr, 0, gImageHeight-1, i);
  959.                 }
  960.                 // force it to write each segment to disk to build the file
  961.                 virtual_buffer_dirty = true;
  962.                 swap_virtual_segment (i);
  963.             }
  964.  
  965.             /* swap back in segment 0 */
  966.             if (gDoingVirtualFile)
  967.             {
  968.                 if (progressDialogPtr)
  969.                     updateProgressDialog(progressDialogPtr, 0, gImageHeight, gImageHeight);
  970.                 virtual_buffer_dirty = true;
  971.                 swap_virtual_segment(0);
  972.             }
  973.  
  974.             if (!gDoingVirtualFile)
  975.             {
  976.                 error_dlogID = 139; // no disk space for swap file
  977.                 dispose_virtual();
  978.             }
  979.         }
  980.  
  981.         SetCursor(&qd.arrow);
  982.  
  983.         // get rid of progress dialog
  984.         if (progressDialogPtr)
  985.             disposeProgressDialog(progressDialogPtr);
  986.  
  987.     } // else virtual
  988.  
  989.     // display any error messages now
  990.     if (error_dlogID)
  991.         (void)displayDialog(error_dlogID, NULL, 0, ewcDoCentering, eSameAsPassedWindow);
  992.     else
  993.     {
  994.         // image window now has content
  995.         // turn on now, so SetImageWindMag will show window
  996.         gImageWindIsValid = true;
  997.  
  998.         // size and show the window
  999.         SetImageWindowMag((**gPrefs2Use_h).imageMagFactor);
  1000.  
  1001.         if (gHasPictUtils)
  1002.             SetCustomPalette(false);
  1003.     }
  1004.  
  1005.     SetCursor(&qd.arrow); // all done initializing..
  1006. } // display_init
  1007.  
  1008.  
  1009. // ==============================================
  1010. // Plot a single pixel at (x,y)
  1011. void display_plot(int x, int y, 
  1012.                      unsigned char Red,
  1013.                      unsigned char Green,
  1014.                      unsigned char Blue)
  1015. {
  1016.     RGBColor c;
  1017.     unsigned char *pptr;
  1018.     Rect    magniRect;
  1019.     long    pport_y;
  1020.     int        mr;
  1021.     int        magFactor;
  1022.  
  1023.     gCurrYPos = y;
  1024.  
  1025.     if (gImagePixmapPtr)
  1026.     {
  1027.         if (gDoingVirtualFile)
  1028.         {
  1029.             swap_virtual_segment(y);
  1030.             pport_y = y - virtual_minY;        /* Y offset within current virtual segment */
  1031.             virtual_buffer_dirty = true;
  1032.         }
  1033.         else
  1034.             pport_y = y;
  1035.  
  1036.         // slam the RGB value into the pixmap
  1037.         pptr = (unsigned char *) 
  1038.                 ((long) gImagePixmapPtr + 4L * ((long) gImageBounds.right * pport_y + (long) x));
  1039.         *pptr++    = 0;
  1040.         *pptr++    = Red;
  1041.         *pptr++    = Green;
  1042.         *pptr    = Blue;
  1043.     }
  1044.  
  1045.     magFactor = (**gPrefs2Use_h).imageMagFactor;
  1046.     if (magFactor != viewmn_hidden)
  1047.     { // it is visible
  1048.         SetPort(gImageWindowPtr);
  1049.         c.red = ((short)Red)<<8;
  1050.         c.green = ((short) Green)<<8;
  1051.         c.blue = ((short) Blue)<<8;
  1052.         if (magFactor == viewmn_normal)
  1053.             SetCPixel(x, y, &c);
  1054.         else
  1055.         { // some magnification at play here
  1056.             if (magFactor != viewmn_Size2Window)
  1057.             { // go to straight magnification N (don't display
  1058.                 magFactor = magFactor-viewmn_normal+1; // 2,3,4
  1059.                 mr = gCurrYPos * magFactor;
  1060.                 magniRect.top        = mr;
  1061.                 magniRect.bottom    = mr + magFactor;
  1062.                 magniRect.left        = x * magFactor;
  1063.                 magniRect.right        = (x+1) * magFactor;
  1064.                 RGBForeColor(&c);
  1065.                 PaintRect(&magniRect);
  1066.             }
  1067.         }
  1068.  
  1069.         // time to update the image?
  1070.         if (TICKS > (gRefreshTick + gDitherTicks))
  1071.         {
  1072.             if (y > (gLastYPos + 1))
  1073.             {
  1074.                 InvalRect_ImageWindow(false);
  1075.                 Cooperate(true);
  1076.                 gLastYPos = y;
  1077.                 gRefreshTick = TICKS;
  1078.             }
  1079.         }
  1080.  
  1081.     }
  1082. } // display_plot
  1083.  
  1084.  
  1085. // ==============================================
  1086. // Do any cleanup needed when the display is complete
  1087. void display_finished(void)
  1088. {
  1089.     // Reset last Y pos so that screen refreshes do the whole screen
  1090.     gLastYPos = 0;
  1091. } // display_finished
  1092.  
  1093.  
  1094. // ==============================================
  1095. // Close any files used after display is complete
  1096. void display_close(void)
  1097. {
  1098. } // display_close
  1099.  
  1100.  
  1101. // ==============================================
  1102. // Find and assign the appropriate custom palette to the image window
  1103. void SetCustomPalette(Boolean doScreenUpdate)
  1104. {
  1105.     PictInfo        plot_info;
  1106.     short            k;
  1107.     OSErr            anError = noErr;
  1108.     PaletteHandle    thePalette = NULL;
  1109.  
  1110.     if (gImageWindowPtr)
  1111.         SetPort(gImageWindowPtr);
  1112.  
  1113.     if (gHasPictUtils && use_custom_palette && gOffScreenPixMapH)
  1114.     {
  1115.         switch(gColorQuantMethod)
  1116.         {
  1117.             case systemMethod:
  1118.             case popularMethod:
  1119.             case medianMethod:
  1120.                 anError = GetPixMapInfo(gOffScreenPixMapH,
  1121.                                 &plot_info,
  1122.                                 returnPalette + suppressBlackAndWhite,
  1123.                                 256,
  1124.                                 gColorQuantMethod,
  1125.                                 0);
  1126.  
  1127.                 // copy the new palette entries into custom palette & use it
  1128.                 if (plot_info.thePalette && gCustomPalette)
  1129.                 {
  1130.                     CopyPalette(plot_info.thePalette, gCustomPalette, 0, 0, 256);
  1131.                     for (k=0; k<256; k++)
  1132.                         SetEntryUsage(gCustomPalette, k, pmTolerant, 0x0000);
  1133.                     thePalette = gCustomPalette;
  1134.                 }
  1135.                 // done with retrieved palette. toolbox created it, so we must destroy it!
  1136.                 if (plot_info.thePalette)
  1137.                     DisposePalette(plot_info.thePalette);
  1138.                 break;
  1139.             otherwise:
  1140.                 SysBeep(4);
  1141.                 break;
  1142.         }
  1143.     }
  1144.     else
  1145.     {
  1146.         // Revert to system palette
  1147.         thePalette = gSystemPalette;
  1148.     }
  1149.  
  1150.     if (gImageWindowPtr)
  1151.     {
  1152.         // reset all screens, this is a little severe though!
  1153.         if (doScreenUpdate)
  1154.             RestoreDeviceClut(NULL);
  1155.         // tell window to use the new palette
  1156.         if (thePalette)
  1157.             NSetPalette(gImageWindowPtr, thePalette, pmAllUpdates);
  1158.         ActivatePalette(gImageWindowPtr);
  1159.     }
  1160.  
  1161. } // SetCustomPalette
  1162.  
  1163.  
  1164. // ==============================================
  1165. // Display the image to the window
  1166. void DrawImageWindow(Boolean DoWholeWindow)
  1167. {
  1168.     BitMap        bmap;
  1169.     short         myMode, i;
  1170.     int            magFactor;
  1171.     static short counter = 0;
  1172.     Rect        my_bounds;
  1173.     RGBColor    myRGBWhite = {-1,-1,-1};
  1174.     RGBColor    myRGBBlack = {0,0,0};
  1175.  
  1176.  
  1177.     SetCursor(&gWaitCursor); // could take a little while..
  1178.  
  1179.     magFactor = (**gPrefs2Use_h).imageMagFactor;
  1180.     if (magFactor != viewmn_hidden)
  1181.     { // it is visible
  1182.         // set these to defaults so copybits behaves predictably
  1183.         RGBBackColor(&myRGBWhite);
  1184.         RGBForeColor(&myRGBBlack);
  1185.  
  1186.         if (DoWholeWindow)
  1187.         {
  1188.             // clear whole window
  1189.             EraseRect(&gImageWindowPtr->portRect);
  1190.         }
  1191.         else
  1192.         {
  1193.         }
  1194.  
  1195.         // something to display?
  1196.         if (gImagePixmapPtr)
  1197.         {
  1198.             bmap.baseAddr = (Ptr) gOffScreenPixMapH;
  1199.             bmap.rowBytes = 0xC000;
  1200.             bmap.bounds = gImageBounds;
  1201.  
  1202. //            if (gHasPictUtils)
  1203. //                SetCustomPalette(false);
  1204.  
  1205.             if ((**gPrefs2Use_h).doDither)
  1206.                 myMode = ditherCopy;
  1207.             else
  1208.                 myMode = srcCopy;
  1209.  
  1210.             if (gDoingVirtualFile)
  1211.             {
  1212.                 magFactor = magFactor-viewmn_normal+1; // 1,2,3,4
  1213.                 for (i=0; i < gImageHeight; i = i + virtual_pixHeight)
  1214.                 {
  1215.                     swap_virtual_segment (i);
  1216.                     my_bounds = gImageBounds;
  1217. // <esp> need to fix up for magFactor in virtual mode
  1218.                     my_bounds.top = (i * magFactor);
  1219.                     my_bounds.bottom = my_bounds.top + (virtual_pixHeight * magFactor);
  1220.                     /* don't worry about the bounds, the clipping region
  1221.                     of the window will stop us from drawing past the bottom */                    
  1222.                     my_bounds.left = 0;
  1223.                     my_bounds.right = gImageWidth * magFactor;
  1224.                     CopyBits(&bmap, &gImageWindowPtr->portBits,
  1225.                                     &gImageBounds, &my_bounds,
  1226.                                     myMode, NULL);
  1227.                 }
  1228.             }
  1229.             else
  1230.             {
  1231.                 CopyBits(&bmap, &gImageWindowPtr->portBits,
  1232.                                 &gImageBounds, &gDisplayBounds,
  1233.                                 myMode, NULL);
  1234.             }
  1235.  
  1236.             // Draw our custom size box if in Size-2-Window mode
  1237.             if (magFactor == viewmn_Size2Window)
  1238.                 DrawImageGrowBox();
  1239.  
  1240.         } // offscreen exists
  1241.      } // it is visible
  1242.  
  1243.     SetCursor(&qd.arrow);
  1244.  
  1245. } // DrawImageWindow
  1246.  
  1247.  
  1248. // ==============================================
  1249. // Write Picture bottleneck routine for paint_to_picture()
  1250. pascal void MyPutPicProc( char *dataPtr, short byteCount)
  1251. {
  1252.     int myByteCount;
  1253.  
  1254.     myByteCount = byteCount;
  1255.     gMyPicSize += byteCount;
  1256.     if (gImagePictFile)
  1257.     {
  1258.         if (fwrite( dataPtr, 1, myByteCount, gImagePictFile) != myByteCount)
  1259.         {
  1260.             fclose (gImagePictFile);
  1261.             gImagePictFile = 0;
  1262.         }
  1263.     }
  1264.     // we have to keep the picture handle header updated for Quickdraw!
  1265.     if (gMyPicHandle)
  1266.         (**gMyPicHandle).picSize = gMyPicSize;
  1267. } // MyPutPicProc
  1268.  
  1269.  
  1270. // ==============================================
  1271. // Write the Pixmap out to either a disk file, or the Clipboard
  1272. void paint_to_picture(short do_disk_buffer)
  1273. {
  1274.     BitMap        bmap;
  1275.     short         myMode, i;
  1276.     OSErr        anError;
  1277.     Rect        my_bounds, myPicRect;    
  1278.     CGrafPort    myCGrafPtr;
  1279.     GrafPtr        oldGrafPtr;
  1280.     PictFHeader    myPictFHeader;
  1281.     CQDProcs    myQDProcs;
  1282.  
  1283.     anError = noErr;
  1284.     if (gImagePixmapPtr)
  1285.     {
  1286.         SetCursor(&gWaitCursor); // could take a little while..
  1287.  
  1288.         GetPort(&oldGrafPtr);
  1289.         OpenCPort (&myCGrafPtr);
  1290.         SetPort ((GrafPtr)&myCGrafPtr);
  1291.  
  1292.         PortSize (gImageWidth, gImageHeight);
  1293.         myPicRect.top = 0;
  1294.         myPicRect.bottom = gImageHeight;
  1295.         myPicRect.left = 0;
  1296.         myPicRect.right = gImageWidth;
  1297.  
  1298.         ClipRect(&myPicRect);
  1299.  
  1300.         if (do_disk_buffer)
  1301.         {
  1302.             SetStdCProcs ((CQDProcsPtr) &myQDProcs);
  1303. #if defined(__powerc)
  1304.             myQDProcs.putPicProc = (QDPutPicUPP)&gPutPICTRD;
  1305. #else
  1306.             myQDProcs.putPicProc = (Ptr) MyPutPicProc;
  1307. #endif // __powerc
  1308.             myCGrafPtr.grafProcs = (CQDProcsPtr) &myQDProcs;
  1309.             // write the PICT header to the file
  1310.             gMyPicSize = sizeof(myPictFHeader);
  1311.             myPictFHeader.picSize = gMyPicSize; // accumulated later in MyPutPicProc()..
  1312.             myPictFHeader.picFrame = myPicRect;
  1313.             i = fwrite (&myPictFHeader, 1, sizeof(myPictFHeader), gImagePictFile);
  1314.             anError = ferror(gImagePictFile);
  1315.         }
  1316.  
  1317.         if (!anError)
  1318.         {
  1319.             /* insure gMyPicHandle null, 'cause MyPutPicProc will be called in OpenPicture! */
  1320.             gMyPicHandle = NULL;
  1321.             gMyPicHandle = OpenPicture (&myPicRect);
  1322.             anError = QDError();
  1323.         }
  1324.  
  1325.         bmap.baseAddr = (Ptr) gOffScreenPixMapH;
  1326.         bmap.rowBytes = 0xC000;    // flag for PixMap
  1327.         if ((**gPrefs2Use_h).doDither)
  1328.             myMode = ditherCopy;
  1329.         else
  1330.             myMode = srcCopy;
  1331.  
  1332.         if (!anError)
  1333.         {
  1334.             if (gDoingVirtualFile)
  1335.                 for (i=0; (i <= gImageHeight) && !anError; i = i + virtual_pixHeight)
  1336.                 {
  1337.                     swap_virtual_segment (i);
  1338.                     my_bounds.top = i;
  1339. // NOTE, JIM! (next source line)
  1340. // By doing a CopyBits of the last rect in full, the whole rect of pixels _does_
  1341. // get written to the PICT file, even though not all the scan lines are valid..
  1342. // this doesn't hurt anything, just makes the file a little bigger than it needs to be,
  1343. // since the extra lines will get clipped on playback.  However, since gImageBounds is
  1344. // global, it would be icky to temporarily change its bottom along with my_bounds so
  1345. // as to only write the necessary lines.. maybe later. :-)
  1346.                     my_bounds.bottom = i + virtual_pixHeight;
  1347.                     my_bounds.left = 0;
  1348.                     my_bounds.right = gImageWidth;
  1349.                     CopyBits(&bmap, (BitMap *)&myCGrafPtr.portPixMap, &gImageBounds, &my_bounds,
  1350.                              myMode, NULL);
  1351.                     anError = QDError();
  1352.                 }
  1353.             else
  1354.                 {
  1355.                 CopyBits(&bmap, (BitMap *)&myCGrafPtr.portPixMap, &gImageBounds, &gImageBounds,
  1356.                          myMode, NULL);
  1357.                 anError = QDError();
  1358.                 }
  1359.         }
  1360.  
  1361.         /* write end-of-picture */
  1362.         if (!anError)
  1363.         {
  1364.             ClosePicture();
  1365.             anError = QDError();
  1366.         }
  1367.  
  1368.         if (!anError)
  1369.         {
  1370.             if (do_disk_buffer)
  1371.             {
  1372.                 /* don't call us anymore! */
  1373.                 SetStdCProcs((CQDProcsPtr) &myQDProcs);
  1374.  
  1375.                 /* move back to pic header again */
  1376.                 fflush(gImagePictFile);
  1377.                 fseek(gImagePictFile, PICTF_HEADER_SIZE, SEEK_SET);
  1378.                 anError = ferror(gImagePictFile);
  1379.                 /* update the PICT header in the file (with now-true picSize field..) */
  1380.                 if (!anError)
  1381.                 {
  1382.                     myPictFHeader.picSize = gMyPicSize;
  1383.                     i = fwrite (&myPictFHeader, 1, sizeof(myPictFHeader), gImagePictFile);
  1384.                     anError = ferror(gImagePictFile);
  1385.                 }
  1386.             }
  1387.             else
  1388.             {
  1389.                 ZeroScrap();
  1390.                 HLock((Handle) gMyPicHandle);
  1391.                 gMyPicSize = GetHandleSize((Handle) gMyPicHandle);
  1392.                 PutScrap (gMyPicSize, 'PICT', (Ptr) *gMyPicHandle);
  1393.                 gMyPicSize = UnloadScrap();
  1394.                 HUnlock((Handle) gMyPicHandle);
  1395.             }
  1396.         } // if !error
  1397.  
  1398.         /* close up shop */
  1399.         CloseCPort(&myCGrafPtr);
  1400.         SetPort(oldGrafPtr);
  1401.         if (gMyPicHandle)
  1402.             DisposeHandle((Handle) gMyPicHandle);
  1403.  
  1404.         /* oh yeah, check for errors */
  1405.         if (anError)
  1406.         {
  1407.             if (gImagePictFile)
  1408.             {
  1409.                 fclose (gImagePictFile);
  1410.                 gImagePictFile = NULL;
  1411.             }
  1412.             SysBeep(4);
  1413.         }
  1414.  
  1415.         SetCursor(&qd.arrow);
  1416.  
  1417.     } // if gImagePixmapPtr..
  1418. } // paint_to_picture
  1419.  
  1420.  
  1421. // ==============================================
  1422. // Write the Pixmap as a PICT file
  1423. // getName - if true, prompt for output file name
  1424. // animSuffix - ignore if <0, if 0-N then use as file name frame suffix for anim
  1425. // ci - QuickTime StdCompression component if image is to be compressed
  1426. void SaveOutputFile(Boolean getName, int animSuffix, ComponentInstance ci)
  1427. {
  1428.     int         k;
  1429.     Rect        myPicRect;    
  1430.     Point         where;
  1431.     SFReply        reply;
  1432.     FInfo        myFileInfo;
  1433.     FSSpec        fsFile;        
  1434.     char        filler[4];
  1435.     char        cFname[256];
  1436.  
  1437.     for (k=0; k<4; k++)
  1438.         filler[k] = 0;
  1439.  
  1440.     // Find and remove the ".POV" suffix
  1441.     pStrCopy(gSrcWind_FileName, (StringPtr)cFname);
  1442.     p2cstr((StringPtr)cFname);
  1443.     k = strlen(cFname);
  1444.     if ((k>4) && (cFname[k-4] == '.'))
  1445.         cFname[k-4] = '\0';
  1446.  
  1447.     // add animation suffix?
  1448.     strcat (cFname, ".");
  1449.     if (animSuffix == kNoAnimSuffix)
  1450.     {
  1451.         strcat (cFname, "Pict");
  1452.     }
  1453.     else
  1454.     {
  1455.         sprintf(filler, "%03d",animSuffix);
  1456.         strcat (cFname, filler);
  1457.     }
  1458.  
  1459.     if (getName)
  1460.     {
  1461.         c2pstr(cFname);
  1462.  
  1463.         GetBestDialogPos(&where);
  1464.         SFPutFile (where, "\pSave PICT file as…", (StringPtr)cFname, NULL, &reply);
  1465.     }
  1466.     else
  1467.     {
  1468.         // copy the generated name into SF Reply..
  1469.         strcpy ((char *) reply.fName,cFname);
  1470.         c2pstr((char *)reply.fName);
  1471.         reply.vRefNum = gSrcWind_VRefNum;
  1472.         reply.good = TRUE;
  1473.     }
  1474.  
  1475.     if (reply.good)
  1476.     {
  1477.         SetCursor(&gWaitCursor);
  1478.  
  1479.         fsFile.vRefNum = reply.vRefNum;
  1480.         fsFile.parID = 0;
  1481.         BlockMove(reply.fName, fsFile.name, 1+reply.fName[0]);
  1482.  
  1483.         p2cstr(reply.fName);
  1484.         SetVol(NULL, reply.vRefNum);
  1485.         gImagePictFile = fopen((char *) reply.fName,"wb");
  1486.         if (gImagePictFile)
  1487.         {
  1488.             for (k=0; k<PICTF_HEADER_SIZE/4; k++)
  1489.                 fwrite (&filler, 1, 4, gImagePictFile);
  1490.  
  1491.             // if not doing a virtual screen (which could take awhile) then
  1492.             // let the window update event get through to repaint the image
  1493.             // in case the dialog stomped on it.
  1494.             if (!gDoingVirtualFile)
  1495.             {
  1496.                 Cooperate(true);
  1497.             }
  1498.  
  1499.             paint_to_picture(TRUE);
  1500.  
  1501.             if (gImagePictFile)
  1502.                 {
  1503.                     fclose (gImagePictFile);
  1504.                     gImagePictFile = NULL;
  1505.                     c2pstr((char *)reply.fName);
  1506.  
  1507.                     /* If QuickTime is around & user wants to squish the picture, ask user how to squish it */
  1508.                     if (gHasImageCompressionMgr && (**gPrefs2Use_h).doCompression)
  1509.                     {
  1510.                         myPicRect.top = 0;
  1511.                         myPicRect.bottom = gImageHeight;
  1512.                         myPicRect.left = 0;
  1513.                         myPicRect.right = gImageWidth;
  1514.                         k = FSMakeFSSpec(reply.vRefNum, 0, reply.fName, &fsFile);
  1515.  
  1516.                         if (!k)
  1517.                             k=CompressPictF(ci, &fsFile);
  1518.                     }
  1519.  
  1520.                     // set image file to type PICT
  1521.                     k = GetFInfo(reply.fName, reply.vRefNum, &myFileInfo);
  1522.                     if (k==0)
  1523.                     {
  1524.                         myFileInfo.fdType = 'PICT';
  1525.                         myFileInfo.fdCreator = (**gPrefs2Use_h).pictFileCreator;
  1526.                         k = SetFInfo (reply.fName, reply.vRefNum, &myFileInfo);
  1527.                     }
  1528.  
  1529. // not yet tested and working, but leave in for later..
  1530. //                    // stick a preview resource on it
  1531. //                    if (wanted..)
  1532. //                        k = AppendFilePreview2PictF(&fsFile);
  1533.  
  1534.                     // Add System 7 custom icons of the image itself to the file
  1535.                     if (gHasImageCompressionMgr)
  1536.                         AppendFinderIcons2PictF(&fsFile, &myPicRect, eAFI_ShrinkWholeImage);
  1537.                 }
  1538.             else
  1539.                 // couldn't write PICT file
  1540.                 (void)displayDialog(140, NULL, 0, ewcDoCentering, eSameAsPassedWindow);
  1541.         }
  1542.         SetCursor(&qd.arrow);
  1543.     }
  1544. } // SaveOutputFile
  1545.